package com.erli.exchange.common.sg;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.erli.exchange.common.sg.config.AnnotationListInfo;
import com.erli.exchange.common.sg.config.BaseClassInfo;
import com.erli.exchange.common.sg.config.BaseInfo;
import com.erli.exchange.common.sg.config.ConverterClassInfo;
import com.erli.exchange.common.sg.config.DaoClassInfo;
import com.erli.exchange.common.sg.config.DaoImplClassInfo;
import com.erli.exchange.common.sg.config.DatasourceInfo;
import com.erli.exchange.common.sg.config.EntityClassInfo;
import com.erli.exchange.common.sg.config.GenerateConfig;
import com.erli.exchange.common.sg.config.ImportListInfo;
import com.erli.exchange.common.sg.config.MapperClassInfo;
import com.erli.exchange.common.sg.config.ModelClassInfo;
import com.erli.exchange.common.sg.config.ServiceClassInfo;
import com.erli.exchange.common.sg.config.ServiceImplClassInfo;
import com.erli.exchange.common.sg.config.TablesInfo;
import com.erli.exchange.common.sg.config.TemplateInfo;
import com.erli.exchange.core.util.JAXBUtil;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 源文件生成抽象类
 * 
 * @title AbstractSourceGenerator.java
 * @package com.example.demo.sourcegenerate
 * @author lihu
 * @update 2019年2月19日 下午5:47:57
 */
@Slf4j
public abstract class AbstractSourceGenerator {

	protected final String DOT_REGEX = "\\.";

	protected final String DOT = ".";

	protected final String COMMA = ",";

	protected final String TAB_NEW_LINE = "\n\t";

	protected final String DOUBLE_TAB_NEW_LINE = "\n\t\t";

	protected final String TAB = "\t";

	protected final String LF = "\n";

	protected final String SLASH = "/";

	protected final String NOTE = "//";

	protected GenerateConfig generateConfig = getGenerateConfig();

	protected BaseInfo baseInfo = generateConfig.getBaseinfo();

	protected TemplateInfo templateInfo = generateConfig.getTemplate();

	protected DatasourceInfo datasourceInfo = generateConfig.getDatasource();

	protected TablesInfo tablesInfo = generateConfig.getTables();

	protected List<String> tableNameList = this.getTableNameList();

	protected EntityClassInfo entityClassInfo = generateConfig.getEntity();

	protected ServiceClassInfo serviceClassInfo = generateConfig.getService();

	protected ServiceImplClassInfo serviceImplClassInfo = generateConfig.getServiceimpl();

	protected DaoClassInfo daoClassInfo = generateConfig.getDao();

	protected DaoImplClassInfo daoImplClassInfo = generateConfig.getDaoimpl();

	protected MapperClassInfo mapperClassInfo = generateConfig.getMapper();
	
	protected ModelClassInfo modelClassInfo = generateConfig.getModel();
	
	protected ConverterClassInfo converterClassInfo = generateConfig.getConverter();
	
	protected final Set<String> importSet = new LinkedHashSet<>();
	protected final Set<String> annotationSet = new LinkedHashSet<>();

	//入口处将表名放入
	protected ThreadLocal<String> tableNameThreadLocal = new ThreadLocal<>();

	/**
	 * 获取JAVA文件中的主要内容
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:08:29
	 */
	protected abstract String getFileMainContent();

	/**
	 * 获取文件的第一行内容
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:08:38
	 */
	protected abstract String getFileFirstLineContent();

	/**
	 * 获取BaseClassInfo的实现类
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 上午11:06:30
	 */
	protected abstract BaseClassInfo getSelfClassInfo();

	/**
	 * 文件存在是否删除
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年2月20日 下午4:45:00
	 */
	protected boolean ifFileExistDelete() {
		return this.getSelfClassInfo().isFileExistsDelete();
	}

	/**
	 * 获取文件的输出路径
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:04:28
	 */
	protected String getOutputDir() {
		String projectLocation = baseInfo.getProjectLocation();
		String module = getSelfClassInfo().getModule();
		String javaFileLocation = baseInfo.getJavaFileLocation();
		String packagePath = this.getSelfClassInfo().getPackagePath();
		return new StringBuilder().append(projectLocation).append(module).append(SLASH).append(javaFileLocation).append(dotToSLASH(packagePath)).toString();
	}

	/**
	 * 获取包路径
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:04:59
	 */
	protected String getPackageName() {
		return this.getSelfClassInfo().getPackagePath();
	}

	/**
	 * 替换所有的.为/
	 * 
	 * @param packagePath
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:05:08
	 */
	protected String dotToSLASH(String packagePath) {
		return packagePath.replaceAll(DOT_REGEX, SLASH);
	}

	/**
	 * 执行清除操作
	 * 
	 * @author lihu
	 * @update 2019年3月8日 下午3:05:13
	 */
	protected void clear() {
		this.importSet.clear();
		this.annotationSet.clear();
	}

	/**
	 * 执行拼装内容前的处理动作
	 * 
	 * @author lihu
	 * @update 2019年3月8日 下午3:05:24
	 */
	protected void doBeforeAssembleFileContent() {
		this.clear();
		BaseClassInfo selfClassInfo = this.getSelfClassInfo();
		ImportListInfo importListInfo = selfClassInfo.getImportListInfo();
		AnnotationListInfo annotationListInfo = selfClassInfo.getAnnotationListInfo();
		if (importListInfo != null && CollectionUtil.isNotEmpty(importListInfo.getImportList())) {
			importListInfo.getImportList().forEach(importItem -> {
				this.importSet.add(importItem.getName());
			});
		}

		if (annotationListInfo != null && CollectionUtil.isNotEmpty(annotationListInfo.getAnnotationList())) {
			annotationListInfo.getAnnotationList().forEach(annotation -> {
				this.annotationSet.add(annotation.getName());
			});
		}
	}

	/**
	 * 组装文件内容
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:05:38
	 */
	protected List<String> assembleFileContent() {
		this.doBeforeAssembleFileContent();
		String fileMainContent = getFileMainContent();
		List<String> result = new ArrayList<String>();
		// 包名
		result.add(String.format(this.templateInfo.getPackageTemplate(), getPackageName()));
		result.add(LF);
		// 导入的包名
		result.addAll(importSet);
		result.add(LF);
		// 注释
		result.add(getTypeComment());
		// 注解
		result.addAll(annotationSet);
		// 类开头
		result.add(getFileFirstLineContent());
		// 主要内容
		result.add(fileMainContent);
		// 类结尾
		result.add(this.templateInfo.getFileEnd());
		return result;
	}

	/**
	 * 获取类注释
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:05:44
	 */
	protected String getTypeComment() {
		String typeCommentTemplate = this.templateInfo.getComment();
		return String.format(typeCommentTemplate, getFileName(), getPackageName(), getAuthor(),
				DateUtil.formatDateTime(new Date()));
	}

	/**
	 * 生成源文件
	 * 
	 * @throws Exception
	 * @author lihu
	 * @update 2019年2月19日 下午7:13:41
	 */
	public void generateSourceFile(String tableName) throws Exception {
		List<String> contentList = assembleFileContent();
		File outputDir = new File(getOutputDir());
		String fileName = this.getFileName();
		if (!outputDir.exists()) {
			outputDir.mkdirs();
		}
		File file = new File(outputDir, fileName);
		if (file.exists()) {
			if (ifFileExistDelete()) {
				file.delete();
			} else {
				log.info(String.format("文件【%s】已经存在", file.getName()));
				return;
			}
		}
		BufferedWriter bw = new BufferedWriter(
				new OutputStreamWriter(new FileOutputStream(new File(outputDir, fileName))));
		for (String item : contentList) {
			bw.write(item);
			bw.write(LF);
		}
		bw.close();
	}

	/**
	 * 获取配置信息
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:05:51
	 */
	public GenerateConfig getGenerateConfig() {
		if (generateConfig != null) {
			return generateConfig;
		}

		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(
					GenerateConfig.class.getClassLoader().getResourceAsStream("generate-config.xml")));
			String line = null;
			StringBuilder stringBuilder = new StringBuilder();
			while ((line = br.readLine()) != null) {
				stringBuilder.append(line);
			}
			String xml = stringBuilder.toString();
			generateConfig = JAXBUtil.fromXML(GenerateConfig.class, xml);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return generateConfig;
	}

	/**
	 * 生成一批同类型的文件
	 * 
	 * @author lihu
	 * @update 2019年3月8日 下午1:39:56
	 */
	public void generateAllSourceFile() {
		this.tableNameList.forEach(tableName -> {
			try {
				this.tableNameThreadLocal.set(tableName);
				this.generateSourceFile(tableName);
				log.info("all done!");
			} catch (Exception e) {
				e.printStackTrace();
			}
		});
	}

	/**
	 * 获取所有表名
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:06:28
	 */
	public List<String> getTableNameList() {
		if (tableNameList != null) {
			return tableNameList;
		}
		tableNameList = new ArrayList<>();
		tablesInfo.getTables().forEach(table -> {
			tableNameList.add(table.getName());
		});
		return tableNameList;
	}

	/**
	 * 获取作者姓名
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:06:34
	 */
	public String getAuthor() {
		return this.baseInfo.getAuthor();
	}

	/**
	 * 获取完整的类路径
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:06:40
	 */
	protected String getFullClassName() {
		return getPackageName().concat(DOT).concat(getClassName());
	}

	/**
	 * 获取类名称
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:06:49
	 */
	protected String getClassName() {
		return getClassNameByBaseClassInfo(getSelfClassInfo());
	}

	/**
	 * 获取JAVA文件带后缀的名称,例如:Example.java
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:06:55
	 */
	protected String getFileName() {
		return getClassName().concat(this.baseInfo.getJavaFileSuffix());
	}

	/**
	 * 指定类名转实例名
	 * 
	 * @param className
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:07:01
	 */
	protected String getInstanceNameByClassName(String className) {
		return className.substring(0, 1).toLowerCase().concat(className.substring(1));
	}

	/**
	 * 获取业务名称
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:07:06
	 */
	protected String getBusinessName() {
		String tableName = tableNameThreadLocal.get();
		return StrUtil.upperFirst(StrUtil.toCamelCase(tableName));
	}

	/**
	 * 获取实体的类名
	 * 
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 上午10:39:51
	 */
	protected String getEntityClassName() {
		return getClassNameByBaseClassInfo(entityClassInfo);
	}

	/**
	 * 根据指定前缀和后缀获取类名
	 * 
	 * @param prefix
	 * @param suffix
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:07:59
	 */
	protected String getClassNameByPrefixAndSuffix(String prefix, String suffix) {
		prefix = prefix == null ? "" : prefix;
		suffix = suffix == null ? "" : suffix;
		return prefix.concat(getBusinessName()).concat(suffix);
	}
	
	public static void main(String[] args) {
		System.out.println("".concat("AnbanQihooVideoImage").concat("Entity"));
	}

	/**
	 * 根据类信息获取类名
	 * 
	 * @param baseClassInfo
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午3:07:47
	 */
	protected String getClassNameByBaseClassInfo(BaseClassInfo baseClassInfo) {
		return getClassNameByPrefixAndSuffix(baseClassInfo.getClassNamePrefix(), baseClassInfo.getClassNameSuffix());
	}

	/**
	 * 获取类的全路径导入
	 * 
	 * @param classNameSuffix
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 上午11:00:45
	 */
	protected String getFullClassImportByBaseClassInfo(BaseClassInfo baseClassInfo) {
		String importTemplate = templateInfo.getImportTempalte();
		return String.format(importTemplate, getFullClassPathByBaseClassInfo(baseClassInfo));
	}

	/**
	 * 获取指定后缀的类全路径
	 * 
	 * @param classNameSuffix
	 * @return
	 * @author lihu
	 * @update 2019年3月8日 下午1:01:48
	 */
	protected String getFullClassPathByBaseClassInfo(BaseClassInfo baseClassInfo) {
		String className = getClassNameByBaseClassInfo(baseClassInfo);
		return baseClassInfo.getPackagePath().concat(DOT).concat(className);
	}

}
